﻿/*
 * SshPacket.java
 *
 * Created on 2007年7月19日, 上午 11:15
 *
 */

package org.uranus.core.ssh;

import java.util.Vector;

/**
 * Copyright 2007 Hsiang-Yu Wang<br>
 * This file is distributed under the terms of the GNU General Public License<br>
 *<br>
 *<pre>
 * --------------------License notice--------------------
 * GNU GENERAL PUBLIC LICENSE
 * Version 3, 29 June 2007
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not,
 * visit: http://www.gnu.org/licenses/gpl.txt
 * --------------------License notice--------------------</pre>
 *  模擬一個符合SSH version 2的packet.
 * @author Hsiang-Yu Wang
 */
abstract public class SshPacket {
    protected int packet_length;
    protected int padding_length;
    
    protected byte[] payload;
    protected byte[] padding;
    
    protected int msg_code;
    private int offset;
    
    //protected Vector payload;
    
    /** Creates a new instance of SshPacket */
    public SshPacket() {
        packet_length = 0;
        padding_length = 0;
        offset = 0;
        
    }
    /** <pre>
     *  init. input packet, if and only if we have received first five bytes. <br>
     *
     *  (packet_length)unit32: -> 4 bytes
     *  (padding_length)byte: -> 1 byte
     *
     *  total bytes => 5 bytes <br>
     *
     *  warning: output packet MUST NOT using this constructor.
     *  reference: RFC4253-section 6
     *  </pre>
     *  @param in
     *         first 5 bytes from input_stream.
     */
    public SshPacket(int[] in){
        packet_length = 0;
        padding_length = 0;
        int i = 0;
        for(; i < 4; i++){
            packet_length += (in[i] & 0xFF) << (8*(3 - i));
        }
        padding_length = in[i];
        payload = new byte[packet_length - padding_length - 1];
        padding = new byte[padding_length];
        offset = 0;
    }
    public SshPacket(byte[] in){
        packet_length = ((in[0] & 0xFF) << 24) + ((in[1] & 0xFF) << 16) + ((in[2] & 0xFF) << 8) + (in[3] & 0xFF);
        padding_length = in[4];
        payload = new byte[packet_length - padding_length - 1];
        padding = new byte[padding_length];
        offset = 0;
    }
    
    abstract public int getPacket_length();
    
    abstract public int getPadding_length();
    
    abstract public void setPacket_length(int packet_length);
    
    abstract public void setPadding_length(int padding_length);
    
    abstract public void addPayload(byte in);
    
    abstract public void addPadding(byte in);
    
    
    public void setPayload(byte[] payload){
        this.payload = payload;
    }
    private void grow(int length){
        if(payload == null){
            payload = new byte[length];
        }else{
            byte[] temp = payload;
            payload = new byte[payload.length + length];
            System.arraycopy(temp, 0, payload, 0, temp.length);
        }
    }
    private void grow(byte[] data){
        grow(data.length);
        System.arraycopy(data,0,payload,payload.length - data.length,data.length);
    }
    
    public void pushByte(byte data){
        grow(new byte[] {data});
    }
    
    public void pushBytes(byte[] data){
        grow(data);
    }
    
    public void pushInt32(int in){
        byte[] b = new byte[4];
        b[0] = (byte)(( in >> 24) & 0xFF);
        b[1] = (byte)(( in >> 16) & 0xFF);
        b[2] = (byte)(( in >> 8 ) & 0xFF);
        b[3] = (byte)(  in        & 0xFF);
        grow(b);
    }
    
    public void pushString(String s){
        pushInt32(s.length());
        grow(s.getBytes());
    }
    public void pushString(byte[] s){
        pushInt32(s.length);
        grow(s);
    }
    public void pushMpint(byte[] data){
        int i = data.length;
        if ((data[0] & 0x80) != 0) {
            pushInt32(++i);
            pushByte((byte) 0);
        }else{
            pushInt32(i);
        }
        grow(data);
    }
    
    public void setMsgcode(int code){
        this.msg_code = code;
    }
    public int getMsgcode(){
        return msg_code;
    }
    
    public byte[] getPayload(int index, int length){
        byte[] out = new byte[length];
        System.arraycopy(payload, index, out, 0 , length);
        return out;
    }
    
    public byte[] getPayload(){
        if(payload == null){
            payload = new byte[0];
        }
        return payload;
    }
    
    public byte getPayload(int index){
        return payload[index];
    }
    
    public int popInt32(){
        byte[] temp = new byte[4];
        System.arraycopy(payload, offset, temp, 0, 4);
        int length = 0;
        for(int i = 0 ; i < temp.length; i++){
            length += (temp[i] & 0xFF) << (8*(3 - i));
        }
        offset += 4;
        return length;
        
    }
    
    public String popString(){
        String out = new String(popBytes());
        return out;        
    }
    
    public byte[] popBytes(){
        int length = popInt32();
        byte[] out = new byte[length];
        System.arraycopy(payload,offset,out,0,length);
        offset += length;
        return out;
    }

    public byte popByte(){
        return payload[offset++];
    }
    
    
}
